/* SListNode.java */

package list;

/**
 *  An SListNode is a mutable node in an SList (singly-linked list).
 **/

public class SListNode extends ListNode {

	/**
	 *  (inherited)  item references the item stored in the current node.
	 *  (inherited)  myList references the List that contains this node.
	 *  next references the next node in the SList.
	 *
	 *  DO NOT CHANGE THE FOLLOWING FIELD DECLARATIONS.
	 **/

	protected SListNode next;

	/**
	 *  SListNode() constructor.
	 *  @param i the item to store in the node.
	 *  @param l the list this node is in.
	 *  @param n the node following this node.
	 */
	SListNode(Object i, SList l, SListNode n) {
		item = i;
		myList = l;
		next = n;
	}

	/** 
	 * SListNode() constructor
	 * @param obj is the item that to be stored in the node.
	 */
	SListNode(Object obj) {
		item = obj;
	
		next = null;
	}


	/**
	 *  next() returns the node following this node.  If this node is invalid,
	 *  throws an exception.
	 *
	 *  @return the node following this node.
	 *  @exception InvalidNodeException if this node is not valid.
	 *
	 *  Performance:  runs in O(1) time.
	 */
	public ListNode next() throws InvalidNodeException {
		if (!isValidNode()) {
			throw new InvalidNodeException("next() called on invalid node");
		}
		if (next == null) {
			// Create an invalid node.
			SListNode node = ((SList) myList).newNode(null, null);
			node.myList = null;
			return node;
		} else {
			return next;
		}
	}

	/**
	 *  prev() returns the node preceding this node.  If this node is invalid,
	 *  throws an exception.
	 *
	 *  @param node the node whose predecessor is sought.
	 *  @return the node preceding this node.
	 *  @exception InvalidNodeException if this node is not valid.
	 *
	 *  Performance:  runs in O(this.size) time.
	 */
	public ListNode prev() throws InvalidNodeException {
		if (!isValidNode()) {
			throw new InvalidNodeException("prev() called on invalid node");
		}
		SListNode prev = ((SList) myList).head;
		if (prev == this) {
			// Create an invalid node.
			prev = ((SList) myList).newNode(null, null);
			prev.myList = null;
		} else {
			while (prev.next != this) {
				prev = prev.next;
			}
		}
		return prev;
	}

	/**
	 *  insertAfter() inserts an item immediately following this node.  If this
	 *  node is invalid, throws an exception.
	 *
	 *  @param item the item to be inserted.
	 *  @exception InvalidNodeException if this node is not valid.
	 *
	 *  Performance:  runs in O(1) time.
	 */
	public void insertAfter(Object item) throws InvalidNodeException {
		if (!isValidNode()) {
			throw new InvalidNodeException("insertAfter() called on invalid node");
		}
		SListNode newNode = ((SList) myList).newNode(item, next);
		if (next == null) {
			((SList) myList).tail = newNode;
		}
		next = newNode;
		myList.size++;
	}

	/**
	 *  insertBefore() inserts an item immediately preceding this node.  If this
	 *  node is invalid, throws an exception.
	 *
	 *  @param item the item to be inserted.
	 *  @exception InvalidNodeException if this node is not valid.
	 *
	 *  Performance:  runs in O(this.size) time.
	 */
	public void insertBefore(Object item) throws InvalidNodeException {
		if (!isValidNode()) {
			throw new InvalidNodeException("insertBefore() called on invalid node");
		}
		SListNode newNode = ((SList) myList).newNode(item, this);
		if (this == ((SList) myList).head) {
			((SList) myList).head = newNode;
		} else {
			SListNode prev = (SListNode) prev();
			prev.next = newNode;
		}
		myList.size++;
	}

	/**
	 *  remove() removes this node from its SList.  If this node is invalid,
	 *  throws an exception.
	 *
	 *  @exception InvalidNodeException if this node is not valid.
	 *
	 *  Performance:  runs in O(this.size) time.
	 */
	public void remove() throws InvalidNodeException {
		if (!isValidNode()) {
			throw new InvalidNodeException("remove() called on invalid node");
		}
		if (this == ((SList) myList).head) {
			((SList) myList).head = next;
			if (next == null) {
				((SList) myList).tail = null;
			}
		} else {
			SListNode prev = (SListNode) prev();
			prev.next = next;
			if (next == null) {
				((SList) myList).tail = prev;
			}
		}
		myList.size--;

		// Make this node an invalid node, so it cannot be used to corrupt myList.
		myList = null;
		// Set other reference to null to improve garbage collection.
		next = null;
	}

}
